package com.xiaobai.frame.config;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.xiaobai.common.utils.result.R;
import com.xiaobai.common.utils.result.Result;
import com.xiaobai.common.utils.result.ResultCode;
import com.xiaobai.common.utils.result.ServiceException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DeadlockLoserDataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 *
 * @author Lion Li
 */
@Slf4j
@RestControllerAdvice
public class ResultExceptionHandler {

//    /**
//     * 权限码异常
//     */
//    @ExceptionHandler(NotPermissionException.class)
//    public Result handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
//        String requestURI = request.getRequestURI();
//        log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
//        return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限，请联系管理员授权");
//    }
//
//    /**
//     * 角色权限异常
//     */
//    @ExceptionHandler(NotRoleException.class)
//    public Result handleNotRoleException(NotRoleException e, HttpServletRequest request) {
//        String requestURI = request.getRequestURI();
//        log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
//        return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限，请联系管理员授权");
//    }
//
//    /**
//     * 认证失败
//     */
//    @ExceptionHandler(NotLoginException.class)
//    public Result handleNotLoginException(NotLoginException e, HttpServletRequest request) {
//        String requestURI = request.getRequestURI();
//        log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
//        return R.fail(HttpStatus.HTTP_UNAUTHORIZED, "认证失败，无法访问系统资源");
//    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Result handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                            HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return R.fail(e.getMessage());
    }

    /**
     * 主键或UNIQUE索引，数据重复异常
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public Result handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage());
        return R.fail("数据库中已存在该记录，请联系管理员确认");
    }

    /**
     * Mybatis系统异常 通用处理
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public Result handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String message = e.getMessage();
        if (StrUtil.contains(message, "CannotFindDataSourceException")) {
            // 未找到数据源
            log.error("请求地址'{}', 未找到数据源：{}", requestURI, message);
            return R.fail("未找到数据源，请联系管理员确认");
        } else if (StrUtil.contains(message, ServiceException.class.getName())) {
            // 全局业务异常直接怕抛出
            log.error("请求地址'{}', 业务异常：{}", requestURI, message);
            return R.fail(e.getCause().getCause().getMessage());
        } else if (StrUtil.contains(message, "UncategorizedSQLException")) {
            log.error("请求地址'{}', SQL参数异常：{}", requestURI, message);
            return R.fail(ResultCode.UNCATEGORIZED_SQL_EXCEPTION);
        } else if (StrUtil.contains(message, "PersistenceException")) {
            log.error("请求地址'{}', ORM持久层异常：{}", requestURI, message);
            return R.fail(ResultCode.PERSISTENCE_EXCEPTION);
        } else if (StrUtil.contains(message, "nested exception is org.apache.ibatis.binding.BindingException: Parameter")) {
            log.error("请求地址'{}', SQL参数异常：{}", requestURI, message);
            return R.fail(ResultCode.PERSISTENCE_EXCEPTION);
        }
        log.error("请求地址'{}', Mybatis系统异常：{}", requestURI, e);
        return R.fail(ResultCode.MYBATIS_EXCEPTION);
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public Result handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage());
        ResultCode code = e.getResultCode();
        return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public Result handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI);
        return R.fail(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI);
        return R.fail(String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
    }

    /**
     * 拦截统一异常
     */
    @ExceptionHandler(RuntimeException.class)
    public Result handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        // 默认未知异常
        ResultCode exceptionCodeEnum = ResultCode.UNKNOWN_EXCEPTION;
        if (e instanceof ClassCastException) {
            // 类型强制转换异常
            exceptionCodeEnum = ResultCode.CLASS_CAST_EXCEPTION;
        } else if (e instanceof NullPointerException) {
            // 空指针异常
            exceptionCodeEnum = ResultCode.NULL_POINTER_EXCEPTION;
        } else if (e instanceof IndexOutOfBoundsException) {
            // 数组下标越界异常
            exceptionCodeEnum = ResultCode.INDEX_OUT_OF_BOUNDS_EXCEPTION;
        } else if (e instanceof NumberFormatException) {
            // Long类型为空异常
            exceptionCodeEnum = ResultCode.LONG_MISS;
        } else if (e instanceof DeadlockLoserDataAccessException) {
            // 数据库死锁异常
            exceptionCodeEnum = ResultCode.DEADLOCK_LOSER_EXCEPTION;
        } else if (e instanceof BadSqlGrammarException) {
            // SQL语法错误
            exceptionCodeEnum = ResultCode.BAD_SQL_GRAMMAR_EXCEPTION;
        } else if (e instanceof DataIntegrityViolationException) {
            if (StrUtil.containsIgnoreCase(e.getMessage(), "doesn't have a default value")) {
                exceptionCodeEnum = ResultCode.SQL_INTEGRITY_CONSTRAINT_VIOLATION_NULL_EXCEPTION;

            }else if (StrUtil.containsIgnoreCase(e.getMessage(), "Data too long for column")){
                exceptionCodeEnum = ResultCode.MYSQL_DATA_TRUNCATION;
            } else if (StrUtil.containsIgnoreCase(e.getMessage(), "Cannot determine value type from")) {
                exceptionCodeEnum = ResultCode.DATA_INTEGRITY_VIOLATION_EXCEPTION;
            } else {
                exceptionCodeEnum = ResultCode.SQL_INTEGRITY_CONSTRAINT_VIOLATION_EXCEPTION;
            }
        }
        log.error("请求地址'{}',发生'{}'.", requestURI, exceptionCodeEnum.getMes(), e);
        return R.fail(exceptionCodeEnum);
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return R.fail(e.getMessage());
    }

    /**
     * 缺少必要参数异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result handleMissingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return R.fail(ResultCode.PARAM_MISS);
    }

    /**
     * 参数解析失败异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Result handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return R.fail(ResultCode.PARAM_PARSE_ERR);
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public Result handleBindException(BindException e) {
        log.error(e.getMessage());
        String message = e.getAllErrors().stream()
            .map(DefaultMessageSourceResolvable::getDefaultMessage).filter(Objects::nonNull).collect(Collectors.joining(", "));
        return R.fail(message);
    }

//    /**
//     * 自定义验证异常
//     */
//    @ExceptionHandler(ConstraintViolationException.class)
//    public Result constraintViolationException(ConstraintViolationException e) {
//        log.error(e.getMessage());
//        String message = e.getConstraintViolations().stream()
//            .map(ConstraintViolationException::getMessage).filter(Objects::nonNull).collect(Collectors.joining(", "));
//        return R.fail(message);
//    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage());
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return R.fail(message);
    }

}
